home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Language/OS - Multiplatform Resource Library
/
LANGUAGE OS.iso
/
cpp_libs
/
intrvews
/
xgrab.lha
/
xgrab
/
grabst
/
create.c
< prev
next >
Wrap
C/C++ Source or Header
|
1990-03-06
|
16KB
|
565 lines
/**
GRAB Graph Layout and Browser System
Copyright (c) 1986, 1988 Regents of the University of California
Copyright (c) 1989, Tera Computer Company
**/
/* create.c -- routines for creating nodes and edges */
#include "attribute.h"
#include "digraph.h"
#include "debug.h"
#include "malloc.h"
DIGRAPH *InitDigraph();
short hash();
NODE *create_node();
int create_edge();
int insert_edge();
VERTEX *insert_vertex();
NODE *insert_node();
VNO get_vno();
char message[100]; /* error messages */
DIGRAPH* InitDigraph()
/* InitDigraph initializes the directed graph, digraph. */
{
DIGRAPH *digraph;
int i; /* counter */
new(digraph, DIGRAPH);
digraph->title = (char *) malloc(256 * sizeof(char));
digraph->title[0] = '\0';
digraph->lastnode = -1; /* no nodes yet */
digraph->levels = NULL; /* no levels yet */
digraph->lastlevel = NULL;
digraph->num_node_attributes = 0;
digraph->num_edge_attributes = 0;
digraph->dist_node_attribute = 0;
digraph->dist_edge_attribute = 0;
digraph->node_att_names = NULL;
digraph->edge_att_names = NULL;
for (i = 0; i < MAXNODES; i++)
{
digraph->nodes[i] = NULL;
}
for (i = 0; i < MAXHASH; i++)
{
digraph->hashtbl[i] = NULL;
}
return(digraph);
} /* InitDigraph */
InitNodeAttrs(digraph, attr_names, num_attr, dist_attr)
DIGRAPH *digraph;
char **attr_names;
int num_attr, dist_attr;
/* Initialize the node user-defined attribute fields for a digraph */
{
digraph->node_att_names = attr_names;
NNodeAttr(digraph) = num_attr;
DNodeAttr(digraph) = dist_attr;
}
InitEdgeAttrs(digraph, attr_names, num_attr, dist_attr)
DIGRAPH *digraph;
char **attr_names;
int num_attr, dist_attr;
/* Initialize the user-defined edge attribute fields for a digraph */
{
digraph->edge_att_names = attr_names;
NEdgeAttr(digraph) = num_attr;
DEdgeAttr(digraph) = dist_attr;
}
NODE *CreateNode(digraph, name, text, x_pos, y_pos)
DIGRAPH *digraph;
char *name;
char *text;
int x_pos, y_pos;
/**
CreateNode puts in default values for shape, brush, color,
status, level, position and the user-defined attributes, and then calls
create_node.
**/
{
SHAPE shape = DEFAULT_SHAPE;
BRUSH brush = DEFAULT_BRUSH;
COLOR color = DEFAULT_COLOR;
STATUS status = NORMAL;
LNO level = -1;
int position = -1;
NODE *node;
int i;
char **attr;
attr = (char **) malloc(NNodeAttr(digraph) * sizeof(char *));
for (i = 0; i < NNodeAttr(digraph); i++)
{
strsave(attr[i], NULL_STRING);
}
dispose(attr[DNodeAttr(digraph)]);
strsave(attr[DNodeAttr(digraph)], text);
node = create_node(digraph, name, attr, shape, brush, color, TRUE,
x_pos, y_pos, status, level, position);
return (node);
} /* CreateNode */
int CreateEdge(digraph, from_node, to_node)
DIGRAPH *digraph;
NODE *from_node, *to_node;
/**
CreateEdge puts in default values for the attributes and then calls
insert_edge
**/
{
int errval; /* returns non-zero value if error */
BRUSH brush = DEFAULT_BRUSH;
COLOR color = DEFAULT_COLOR;
char **attr;
int i;
attr = (char **) malloc(NEdgeAttr(digraph) * sizeof(char *));
for (i = 0; i < NEdgeAttr(digraph); i++)
{
strsave(attr[i], NULL_STRING);
}
errval = insert_edge(digraph, Vno(from_node), Vno(to_node), attr, brush,
color);
return (errval);
} /* CreateEdge */
NODE *create_node(digraph, name, attr, shape, brush, color, disp, x_pos, y_pos,
status, level, position)
DIGRAPH *digraph;
char *name; /* name of node */
char **attr; /* user-defined attributes for the node */
SHAPE shape; /* shape of node */
BRUSH brush; /* how to draw it */
COLOR color; /* color to draw it */
BOOL disp; /* display it or not */
int x_pos, y_pos; /* initial x and y positions, if specified */
STATUS status; /* status of node: NORMAL, DUMMY, etc. */
LNO level; /* level number */
int position; /* position on level */
/**
create_node creates a vertex and a node and initializes the
name, shape, brush, color, attributes, x and y positions, status, level,
and position on level. It returns a pointer to the node.
If the vertex and node already exist, the values of attributes, shape,
etc are changed to the values passed to create_node.
Currently, the level and position are ignored.
**/
{
VERTEX *vertex; /* pointer to vertex */
NODE *node; /* pointer to node */
vertex = insert_vertex(digraph, name);
node = insert_node(digraph, vertex, attr, shape, brush, color, disp,
x_pos, y_pos, status);
return (node);
} /* create_node */
VERTEX* insert_vertex(digraph, name)
DIGRAPH *digraph;
char *name; /* name of the vertex to add */
/**
insert_vertex returns the pointer to the vertex named name.
If there is no such vertex, a new one is created.
**/
{
short hashcode; /* hash code for name */
VERTEX *curvertex; /* used to chase down the vertex chain */
if (debug)
{
printf("insert_vertex: '%s': ", name);
}
hashcode = hash(name);
/* first chase down the list, if any */
for (curvertex = digraph->hashtbl[hashcode];
(curvertex != NULL) && (strcmp(curvertex->name, name) != 0);
curvertex = curvertex->next)
{}
if (curvertex != NULL) /* found it */
{
if (debug)
{
printf("found '%s'\n", curvertex->name);
}
return (curvertex);
}
if (debug)
{
if (digraph->hashtbl[hashcode] == NULL) /* start the vertex chain */
{
printf("started a chain.\n");
}
else /* add to the head of the chain */
{
printf("added to chain.\n");
}
}
new(curvertex, VERTEX); /* link in a new vertex */
curvertex->next = digraph->hashtbl[hashcode];
digraph->hashtbl[hashcode] = curvertex;
strsave(curvertex->name, name);
curvertex->vno = -1; /* no node number assigned yet */
return (curvertex);
} /* insert_vertex */
NODE *insert_node(digraph, vertex, attr, shape, brush, color, disp,
x_pos, y_pos, status)
DIGRAPH *digraph;
VERTEX *vertex; /* vertex info */
char **attr; /* user-defined attributes for the node */
SHAPE shape; /* shape of node */
BRUSH brush; /* how to draw it */
COLOR color; /* color to draw it */
BOOL disp; /* display it or not */
int x_pos, y_pos; /* x and y positions */
STATUS status; /* type of node */
/**
adds a node to digraph. The new vertex number is put into vertex->vno,
and the pointer to the node is returned.
If the node already exists, its attributes, shape, etc are updated.
**/
{
NODE *node; /* pointer to the new node */
if (++digraph->lastnode == MAXNODES)
{
PrintError("insert_node: Too many nodes");
return (NULL);
}
if (vertex->vno == -1) /* new vertex, need to create node */
{
new(node, NODE);
digraph->nodes[digraph->lastnode] = node;
vertex->vno = digraph->lastnode;
node->vertex = vertex;
node->coalescer_vno = -1;
node->coalesced = FALSE;
node->coalescer = FALSE;
node->out_edges = NULL;
node->in_edges = NULL;
node->attributes = attr;
init_set(Expansion(node));
init_set(Succ_set(node));
init_set(Ante_set(node));
init_member(Node_member(node));
}
else /* existing vertex, reuse node */
{
node = Node(digraph, vertex->vno);
dispose_attr(node->attributes, NNodeAttr(digraph));
node->attributes = attr;
}
X_position(node) = x_pos;
Y_position(node) = y_pos;
Shape(node) = shape;
Brush(node) = brush;
Color(node) = color;
Status(node) = status;
Displayed(node) = disp;
/**
The half_width and half_height of the node depend on the shape and on
the length of the "Text". The following formulas are preliminary.
**/
switch (shape)
{
case POINT:
Half_width(node) = DUMMY_HALF_WIDTH;
Half_height(node) = 0;
break;
case DIAMOND:
Half_width(node) = Node_half_width(node) + HALF_CHAR * 2;
/* 2 extra chars to avoid pinching chars at the end */
Half_height(node) = NODE_HALF_HEIGHT + 2;
break;
case CIRCLE:
Half_width(node) = Node_half_width(node);
Half_height(node) = NODE_HALF_HEIGHT + 2;
/* circle drawing routines should know to make it a circle. */
break;
case OVAL:
Half_width(node) = Node_half_width(node);
Half_height(node) = NODE_HALF_HEIGHT + 2;
break;
case DOUBLE_BOX:
case RECTANGLE:
Half_width(node) = Node_half_width(node);
Half_height(node) = NODE_HALF_HEIGHT;
break;
default:
PrintError("insert_node: illegal shape");
break;
}
return (node);
} /* insert_node */
init_member(member)
MEMBER *member;
/* init_member initializes the fields in member. */
{
Last_dummy(member) = 0; /* has no dummies yet */
Level_no(member) = -1; /* no level yet */
Position(member) = -1; /* no position on level yet */
Status(member) = NORMAL;
Is_layed(member) = FALSE; /* not layed out yet */
Bc(member, UP) = Bc(member, DOWN) = NO_BC;
Priority(member, UP) = Priority(member, DOWN) = 0;
Prev_member(member) = Next_member(member) = NULL;
} /* init_member */
int create_edge(digraph, from_name, to_name, attr, brush, color, reverse)
DIGRAPH *digraph;
char *from_name, *to_name; /* names of from and to nodes */
char **attr; /* user-defined attributes for edge */
BRUSH brush; /* how to draw it */
COLOR color; /* color to draw it */
BOOL reverse; /* reverse the edge */
/**
create_edge adds the edge from from_name to to_name with attributes attr
to digraph->. If from_name or to_name do not exist, an error message
is printed and the procedure returns a non-zero integer value.
This procedure checks for valid names and then calls insert_edge.
**/
{
VNO from_vno, to_vno; /* from and to vertex numbers */
int errval; /* non-zero if error */
from_vno = get_vno(digraph, from_name);
if (from_vno == -1)
{
sprintf(message, "create_edge: From Node \"%s\" not defined",
from_name);
PrintError(message);
return (-1);
}
to_vno = get_vno(digraph, to_name);
if (to_vno == -1)
{
sprintf(message, "create_edge: To Node \"%s\" not defined", to_name);
PrintError(message);
return (-1);
}
/* reverse the edge, but only if these are 'real' nodes */
if (reverse && !Is_dummy(Node(digraph, to_vno)) &&
!Is_dummy(Node(digraph, from_vno)))
{
errval = insert_edge(digraph, to_vno, from_vno, attr, brush, color);
reverse_edge(digraph, to_vno, from_vno,
max_edges(Node(digraph, to_vno),
Node(digraph, from_vno)));
}
else
{
errval = insert_edge(digraph, from_vno, to_vno, attr, brush, color);
}
return (errval);
} /* create_edge */
int insert_edge(digraph, from_vno, to_vno, attr, brush, color)
DIGRAPH *digraph;
VNO from_vno, to_vno;
char **attr;
BRUSH brush; /* how to draw it */
COLOR color; /* color to draw it */
/**
insert_edge adds the edge from from_vno to to_vno with attributes attr
to digraph->. If the status of the nodes containing the edge is invalid,
a non-zero integer value is returned.
Edges from a node to itself are disallowed.
**/
{
NODE *from_node, *to_node;
int ord;
from_node = Node(digraph, from_vno);
to_node = Node(digraph, to_vno);
if (from_vno == to_vno)
{
fprintf(stderr,
"Cannot add edge from a node to itself: Edge %s %s ignored\n",
Name(from_node), Name(from_node));
return (-1);
}
/**
change the width of the nodes if necessary to make room for all
the edges
**/
ord = max_edges(from_node, to_node) + 1;
fix_width(from_node, ord);
fix_width(to_node, ord);
/**
out and in edges are only added between normal nodes.
Dummy nodes just have successor and predecessor sets.
Coalescer nodes also have just successor and predecessor sets,
but we don't need to worry about them here because you can't add
an edge to/from a coalescer node.
In essence, there is a distinction between the in and out edges,
which correspond to the abstract graph, and the predecessor and
successor sets, which correspond to the displayed graph. In most
cases, the predecessor and successor sets should be directly
accessed, while the in and out edges are accessed only indirectly,
usually by routines in util.c.
**/
if (Status(from_node) == NORMAL && Status(to_node) == NORMAL)
{
add_out_edge(from_node, attr, brush, color, to_vno, ord);
add_in_edge(to_node, from_vno, ord);
add_element(from_node->succ_set, to_vno);
add_element(to_node->ante_set, from_vno);
}
else if (Is_dummy(from_node) || Is_dummy(to_node))
{
add_element(from_node->succ_set, to_vno);
add_element(to_node->ante_set, from_vno);
}
else
{
sprintf(message, "insert_edge: Illegal status in node %s or %s",
Name(from_node), Name(to_node));
PrintError(message);
return (-1);
}
return (0);
} /* insert_edge */
fix_width(node, ord)
NODE *node;
int ord;
/* make sure this node has enough room for ord edges */
{
Half_width(node) = MAX(EDGE_GAP * ord / 2, Half_width(node));
}
add_out_edge(from_node, attr, brush, color, to_vno, ord)
NODE *from_node; /* node with the edge list */
char **attr; /* user-defined attributes for the edge */
BRUSH brush; /* how to draw it */
COLOR color; /* color to draw it */
VNO to_vno; /* vertex number of target */
int ord; /* this is the ordth edge from from_node to to_vno */
/**
add_out_edge adds an edge to out_edges.
**/
{
OUTEDGE *curedge; /* the new edge */
new(curedge, OUTEDGE); /* link in a new edge */
curedge->next = from_node->out_edges;
from_node->out_edges = curedge;
Brush(curedge) = brush;
Color(curedge) = color;
curedge->attributes = attr;
curedge->edge_reversed = FALSE;
curedge->to_vno = to_vno;
curedge->ordinality = ord;
} /* add_out_edge */
add_in_edge(to_node, from_vno, ord)
NODE *to_node; /* node with the incoming edge list */
VNO from_vno; /* vertex number of source node */
int ord; /* this is the ordth edge from from_node to to_vno */
/**
add_in_edge adds an edge to in_edges. Note that all the attributes
for an edge are kept in the outedges, to save space and make updates
simpler
**/
{
INEDGE *curedge; /* the new edge */
new(curedge, INEDGE); /* link in a new edge */
curedge->next = to_node->in_edges;
to_node->in_edges = curedge;
curedge->edge_reversed = FALSE;
curedge->from_vno = from_vno;
curedge->ordinality = ord;
} /* add_in_edge */
VNO get_vno(digraph, name)
DIGRAPH *digraph;
char *name; /* name of the vertex to search for */
/**
get_vno returns the vertex number for the vertex named name.
If there is no such vertex, the value -1 is returned.
**/
{
short hashcode; /* hash code for name */
VERTEX *curvertex; /* used to chase down the vertex chain */
hashcode = hash(name);
/* first chase down the list, if any */
for (curvertex = digraph->hashtbl[hashcode];
(curvertex != NULL) && (strcmp(curvertex->name, name) != 0);
curvertex = curvertex->next)
{}
if (curvertex != NULL) /* found it */
{
return(curvertex->vno);
}
else
{
return ( (VNO) -1);
}
} /* get_vno */
short hash(name)
char *name; /* characters to hash together */
/**
hash returns the hash code for name.
**/
{
short i; /* counter */
short hashcode; /* computed hash code */
for (i = 0, hashcode = 0; name[i] != '\0'; hashcode+= name[i], i++)
{}
hashcode = hashcode % MAXHASH;
return (hashcode);
} /* hash */